home *** CD-ROM | disk | FTP | other *** search
Text File | 1996-09-17 | 46.8 KB | 1,603 lines | [TEXT/MPS ] |
- //========================================================================================
- //
- // File: SLStrRep.cpp
- // Release Version: $ ODF 2 $
- //
- // Copyright: (c) 1993 - 1996 by Apple Computer, Inc., all rights reserved.
- //
- //========================================================================================
-
- #include "FWFound.hpp"
-
- #ifndef PRSTRREP_H
- #include "PRStrRep.h"
- #endif
-
- #ifndef SLPRIDEB_H
- #include "SLPriDeb.h"
- #endif
-
- #ifndef SLPRIMEM_H
- #include "SLPriMem.h"
- #endif
-
- // ----- Foundation Includes -----
-
- #ifndef FWPRIDEB_H
- #include "FWPriDeb.h"
- #endif
-
- #ifdef FW_BUILD_MAC
- #include <OSA.h>
- #endif
-
- #ifdef FW_BUILD_MAC
- #include <Script.h>
- #endif
-
- #ifndef _ERRORDEF_
- #include "ErrorDef.xh"
- #endif
-
- #ifdef FW_BUILD_MAC
- #include <TextUtils.h>
- #endif
-
- #ifdef FW_BUILD_MAC
- #include <fp.h>
- #endif
-
- #ifdef FW_BUILD_MAC
- #pragma segment Strings
- #endif
-
- //========================================================================================
- // gEmptyStringRep
- //========================================================================================
-
- static FW_Locale gDefaultLocaleData =
- {
- #ifdef FW_BUILD_MAC
- smRoman, // fScriptCode
- langEnglish // fLangCode
- #endif
- #ifdef FW_BUILD_WIN
- 0,
- 0
- #endif
- };
-
- static FW_SPrivStringRep gEmptyStringRep =
- {
- { // ODIText fText;
- kODTraditionalMacText, // format
- {
- sizeof(gDefaultLocaleData), // _maximum
- sizeof(gDefaultLocaleData), // _length
- (unsigned char*) &gDefaultLocaleData // _buffer
- }
- },
- { // FW_ODITextParams fParams;
- (char*) &gDefaultLocaleData+sizeof(gDefaultLocaleData), // fTextStart
- 0, // fTextByteLength
- 0 // fTextByteCapacity
- },
- 100, // fRefCount
- true, // fUsingStaticBuffer
- false // fLocked
- };
-
- //----------------------------------------------------------------------------------------
- // FW_PrivInitStringData - call once after static initialization
- //----------------------------------------------------------------------------------------
-
- void FW_PrivInitStringData()
- {
- // Set the gDefaultLocaleData to the system script and language
- #ifdef FW_BUILD_MAC
- gDefaultLocaleData.fScriptCode = ::GetScriptManagerVariable(smSysScript);
- gDefaultLocaleData.fLangCode = ::GetScriptVariable(gDefaultLocaleData.fScriptCode, smScriptLang);
- #endif
- #ifdef FW_BUILD_WIN
- // Not yet implemented
- #endif
- }
-
- //----------------------------------------------------------------------------------------
- // FW_PrivGetDefaultLocale - return default (system) locale
- //----------------------------------------------------------------------------------------
-
- FW_Locale FW_PrivGetDefaultLocale()
- {
- return gDefaultLocaleData;
- }
-
- //========================================================================================
- // FW_SPrivStringRep Functions
- //========================================================================================
-
- //----------------------------------------------------------------------------------------
- // FW_Check
- //----------------------------------------------------------------------------------------
-
-
- #ifdef FW_DEBUG
- static void FW_Check(FW_HString self, long minRefCount)
- {
- // No need for FW_ERR_TRY block
- FW_PlatformError error;
- FW_PRIV_ASSERT(self);
- FW_PRIV_ASSERT(self->fRefCount>=minRefCount);
- FW_PRIV_ASSERT(0 <= self->fText.text._length);
- FW_PRIV_ASSERT(self->fText.text._length <= self->fText.text._maximum);
- FW_PRIV_ASSERT(self->fText.text._buffer!=0);
-
- FW_ODITextParams params;
- FW_TextParams_GetParams(&self->fText, ¶ms, &error);
- FW_PRIV_ASSERT(error == 0);
- FW_PRIV_ASSERT(self->fParams.fTextStart == params.fTextStart);
- FW_PRIV_ASSERT(self->fParams.fTextByteLength == params.fTextByteLength);
- FW_PRIV_ASSERT(self->fParams.fTextByteCapacity == params.fTextByteCapacity);
- FW_PRIV_ASSERT(self->fParams.fTextByteLength <= self->fParams.fTextByteCapacity);
- }
- #define FW_CHECK(self) FW_Check(self, 1);
- #else
- #define FW_CHECK(self)
- #endif
-
- #ifdef FW_DEBUG
- #define CHECK_ERROR if (*error) { Debugger(); return 0; } else 0
- #else
- #define CHECK_ERROR if (*error) { return 0; } else 0
- #endif
-
- //----------------------------------------------------------------------------------------
- // StringRep_Clone
- //----------------------------------------------------------------------------------------
- // Conditionally creates a copy. If the reference count of the
- // self is exactly one, or if self is locked, then self is simply returned, unchanged.
- // If the reference count of self is greater than one, then
- // its contents are cloned, and self's reference count is decremented.
- // The reference count of the copy is set to 1.
- // Note that this function conserves the total reference count.
-
- // If a clone is performed out, there are two possible outcomes. This illustration
- // demonstrates the semantics of cloning when two string objects share a common
- // representation. For simplification, there are only two strings involved -
- // the string being cloned, and one other string. In practice, there can
- // be additional clients of the common representation.
-
- // Prior to cloning, the object graph looks as follows:
- //
- // cloneString otherString
- // | |
- // \ /
- // \ /
- // \ /
- // rep1
- // |
- // |
- // buffer1
-
- // Depending on whether or not the representation object is the empty string,
- // there will be one of two outcomes. The first illustration depicts the outcome
- // when the representation object is the empty string. First a new representation
- // object is created. A new buffer is created. The new representation is initialized
- // to point at the new buffer. The second illustration depicts the outcome when
- // the representation object is not the empty string. First a new representation
- // object is created. The new representation is initialized to point at the
- // existing buffer. Then a new buffer is created. The old representation object
- // is set to point at the new buffer. The data from the old buffer is then
- // copied into the new buffer. This logic is required specifically to prevent
- // dynamic strings from pointing at buffers owned by bound strings, while the
- // bounds strings point at heap-based buffers.
-
- // cloneString otherString
- // | |
- // | |
- // rep2 rep1
- // | |
- // | |
- // buffer2 buffer1
-
-
- // cloneString otherString
- // | |
- // | |
- // rep2 rep1
- // | |
- // | |
- // buffer1 buffer2
-
- static FW_HString StringRep_Clone(FW_HString self, FW_PlatformError *error);
-
- static FW_HString StringRep_Clone(FW_HString self, FW_PlatformError *error)
- {
- *error = 0;
- FW_CHECK(self);
-
- FW_PRIV_ASSERT(self->fRefCount>=1);
- FW_HString clone = self;
- if ((!self->fLocked) && (self->fRefCount > 1))
- {
- FW_HString newBuffer;
- FW_HString oldBuffer;
-
- clone = (FW_SPrivStringRep*)FW_PrimitiveAllocateBlock(sizeof(FW_SPrivStringRep));
- if (clone == NULL)
- {
- *error = kODErrOutOfMemory;
- return 0;
- }
-
- *clone = *self;
-
- // If the representation being cloned is the empty string, then
- // the clone
- if (self == &gEmptyStringRep)
- {
- newBuffer = clone;
- oldBuffer = self;
- }
- else
- {
- newBuffer = self;
- oldBuffer = clone;
- }
-
- newBuffer->fRefCount = 0;
- newBuffer->fText.text._buffer = (unsigned char*)
- FW_PrimitiveAllocateBlock(oldBuffer->fText.text._maximum);
- if (newBuffer->fText.text._buffer == NULL)
- {
- *error = kODErrOutOfMemory;
- return 0;
- }
- newBuffer->fUsingStaticBuffer = false;
- newBuffer->fLocked = false;
- ::FW_PrimitiveCopyMemory(oldBuffer->fText.text._buffer,
- newBuffer->fText.text._buffer,
- oldBuffer->fText.text._length);
- ::FW_TextParams_GetParams(&newBuffer->fText, &newBuffer->fParams, error);
- CHECK_ERROR;
- ::FW_PrivString_Acquire(newBuffer);
- ::FW_PrivString_Release(oldBuffer);
- }
-
- FW_CHECK(self);
-
- return clone;
- }
- //----------------------------------------------------------------------------------------
- // FW_PrivString_NewRepWithStaticBuffer
- //----------------------------------------------------------------------------------------
-
- FW_HString FW_PrivString_NewRepWithStaticBuffer(unsigned char* buffer,
- FW_ByteCount bufferLen)
- {
- FW_HString rep = (FW_SPrivStringRep*)
- FW_PrimitiveAllocateBlock(sizeof(FW_SPrivStringRep));
- if (rep != NULL)
- {
- FW_PRIV_ASSERT(bufferLen > sizeof(FW_Locale));
- FW_Locale* locale = (FW_Locale*) buffer;
- *locale = gDefaultLocaleData;
-
- rep->fText.format = kODTraditionalMacText;
- rep->fText.text._buffer = buffer;
- rep->fText.text._maximum = bufferLen;
- rep->fText.text._length = sizeof(gDefaultLocaleData);
-
- rep->fParams.fTextStart = (char*) buffer + sizeof(gDefaultLocaleData);
- rep->fParams.fTextByteLength = 0;
- rep->fParams.fTextByteCapacity = bufferLen - sizeof(gDefaultLocaleData);
-
- rep->fRefCount = 0;
- rep->fUsingStaticBuffer = true;
- rep->fLocked = false;
- }
-
- return rep;
- }
-
- //----------------------------------------------------------------------------------------
- // FW_PrivString_ReleaseStaticBuffer
- //----------------------------------------------------------------------------------------
-
- void FW_PrivString_ReleaseStaticBuffer(FW_HString self, FW_PlatformError* error)
- {
- // This function should only be called from the destructor of bounded string
- // classes. When a bounded string class is being destroyed, the static buffer
- // can no longer be used. There are several cases that must be considered:
- //
- // 1) The StringRep is no longer using the static buffer because some
- // operation caused the buffer to be reallocated (e.g. an append operation
- // caused the string to grow beyond the capacity of the buffer). In this
- // case, the buffer has already been released, and nothing needs to be done.
- //
- // 2) The StringRep is still using the static buffer, but the reference
- // count is 1. In that case, the last reference is about to be released
- // and the whole rep will be blown away. We don't need to save the contents
- // of the buffer, and we especially do not want to clear the fUsingStaticBuffer
- // flag, because that would cause FW_PrivString_Release to erroneously attempt
- // to delete the buffer, which would likely corrupt the heap. This means
- // the correct thing is again to do nothing.
- //
- // 3) The stringRep is still using the static buffer, but the reference
- // count is greater than 1. In that case, a dynamic buffer must be allocated
- // for the contents of the string. We can safely mark the string as no longer
- // using the static buffer (i.e. set fUsingStaticBuffer to false), since
- // FW_PrivString_Release will only decrement the reference count and not
- // delete the rep.
-
- *error = 0;
- FW_CHECK(self);
-
- if (self->fRefCount>1 && self->fUsingStaticBuffer)
- {
- unsigned char* newBuffer = (unsigned char*)
- FW_PrimitiveAllocateBlock(self->fText.text._maximum);
- if (newBuffer == NULL)
- {
- *error = kODErrOutOfMemory;
- return;
- }
- ::FW_PrimitiveCopyMemory(self->fText.text._buffer,
- newBuffer,
- self->fText.text._length);
- self->fText.text._buffer = newBuffer;
- self->fUsingStaticBuffer = false;
- ::FW_TextParams_GetParams(&self->fText, &self->fParams, error);
- }
- }
-
- //----------------------------------------------------------------------------------------
- // FW_PrivString_LockString
- //----------------------------------------------------------------------------------------
-
- FW_HString FW_PrivString_LockString(FW_HString self, FW_PlatformError* error)
- {
- FW_HString rep = self;
- if (!rep->fLocked)
- {
- rep = ::StringRep_Clone(rep, error);
- if (*error != 0)
- return 0;
- rep->fLocked = true;
- rep->fRefCount = 0;
- }
- return rep;
- }
-
- //----------------------------------------------------------------------------------------
- // FW_PrivString_AcquireEmptyString
- //----------------------------------------------------------------------------------------
-
- FW_HString FW_PrivString_AcquireEmptyString()
- {
- FW_HString rep = &gEmptyStringRep;
- ::FW_PrivString_Acquire(rep);
- return rep;
- }
-
- //----------------------------------------------------------------------------------------
- // FW_PrivString_AcquireEmptyStringWithLocale
- //----------------------------------------------------------------------------------------
-
- FW_HString FW_PrivString_AcquireEmptyStringWithLocale(FW_Locale locale, FW_PlatformError* error)
- {
- FW_HString rep = &gEmptyStringRep;
- ::FW_PrivString_Acquire(rep);
- rep = ::StringRep_Clone(rep, error);
- if (*error != 0)
- return 0;
- ::FW_TextParams_SetLocale(&rep->fText, locale, &rep->fParams, error);
- if (*error != 0)
- return 0;
- #ifdef FW_DEBUG
- ::FW_Check(rep, 0);
- #endif
- return rep;
- }
-
- //----------------------------------------------------------------------------------------
- // FW_PrivString_Acquire
- //----------------------------------------------------------------------------------------
-
- void FW_PrivString_Acquire(FW_HString self)
- {
- #ifdef FW_DEBUG
- ::FW_Check(self, 0);
- #endif
- ++self->fRefCount;
- }
-
- //----------------------------------------------------------------------------------------
- // FW_PrivString_Release
- //----------------------------------------------------------------------------------------
-
- void FW_PrivString_Release(FW_HString self)
- {
- FW_CHECK(self);
- if(--self->fRefCount == 0)
- {
- if (self->fLocked)
- {
- self->fLocked = false;
- self->fRefCount = 1;
- }
- else
- {
- if (!self->fUsingStaticBuffer)
- FW_PrimitiveFreeBlock(self->fText.text._buffer);
- FW_PrimitiveFreeBlock(self);
- }
- }
- }
-
- //----------------------------------------------------------------------------------------
- // FW_PrivString_GetByteLength
- //----------------------------------------------------------------------------------------
-
- FW_ByteCount FW_PrivString_GetByteLength(FW_HString self)
- {
- FW_CHECK(self);
- return self->fParams.fTextByteLength;
- }
-
- //----------------------------------------------------------------------------------------
- // FW_PrivString_GetCapacity
- //----------------------------------------------------------------------------------------
-
- FW_ByteCount FW_PrivString_GetCapacity(FW_HString self)
- {
- FW_CHECK(self);
- return self->fParams.fTextByteCapacity;
- }
-
- //----------------------------------------------------------------------------------------
- // FW_PrivString_GetCharacterLength
- //----------------------------------------------------------------------------------------
-
- FW_CharacterCount FW_PrivString_GetCharacterLength(FW_HString self, FW_PlatformError* error)
- {
- FW_CHECK(self);
- FW_CharacterCount result = self->fParams.fTextByteLength;// assume singlebyte locale
- if (!::FW_LocaleIsSingleByte(self->fParams.fTextLocale))
- {
- FW_BytePosition knownStart = 0;
- FW_BytePosition position = 0;
- result = 0;
- while (position < self->fParams.fTextByteLength)
- {
- FW_Boolean isStart = ::FW_TextParams_IsCharacterStart(&self->fText, knownStart, position, error);
- if (*error)
- return 0;
- if (isStart)
- {
- knownStart = position;
- result++;
- }
- position++;
- }
- }
- return result;
- }
-
- //----------------------------------------------------------------------------------------
- // FW_PrivString_SetCapacity
- //----------------------------------------------------------------------------------------
- FW_HString FW_PrivString_SetCapacity(FW_HString self,
- FW_ByteCount newCapacity,
- FW_PlatformError* error)
- {
- FW_HString rep = ::StringRep_Clone(self, error);
- if (*error)
- return 0;
-
- ::FW_TextParams_SetCapacity(&rep->fText,
- newCapacity,
- !rep->fUsingStaticBuffer,
- &rep->fParams,
- error);
- return rep;
- }
-
- //----------------------------------------------------------------------------------------
- // FW_PrivString_RevealODIText
- //----------------------------------------------------------------------------------------
-
- ODIText* FW_PrivString_RevealODIText(FW_HString self)
- {
- FW_CHECK(self);
- return &self->fText;
- }
-
- //----------------------------------------------------------------------------------------
- // FW_PrivString_RevealBuffer
- //----------------------------------------------------------------------------------------
-
- const char* FW_PrivString_RevealBuffer(FW_HString self)
- {
- FW_CHECK(self);
- return self->fParams.fTextStart;
- }
-
- //----------------------------------------------------------------------------------------
- // FW_PrivString_RevealLocale
- //----------------------------------------------------------------------------------------
- FW_Locale* FW_PrivString_RevealLocale(FW_HString self)
- {
- FW_CHECK(self);
- return &self->fParams.fTextLocale;
- }
-
- //----------------------------------------------------------------------------------------
- // FW_PrivString_GetLocale
- //----------------------------------------------------------------------------------------
-
- void FW_PrivString_GetLocale(FW_HString self, FW_Locale* locale)
- {
- FW_CHECK(self);
- *locale = self->fParams.fTextLocale;
- }
-
- //----------------------------------------------------------------------------------------
- // FW_PrivString_Retrieve
- //----------------------------------------------------------------------------------------
-
- void FW_PrivString_Retrieve(FW_HString self,
- char* destination,
- FW_ByteCount numberBytes,
- FW_BytePosition position)
-
- {
- FW_CHECK(self);
- ::FW_PrimitiveCopyMemory(self->fParams.fTextStart+position, destination, numberBytes);
- }
-
- //----------------------------------------------------------------------------------------
- // FW_PrivString_Delete
- //----------------------------------------------------------------------------------------
-
- FW_HString FW_PrivString_Delete(FW_HString self,
- FW_ByteCount numberBytes,
- FW_BytePosition position,
- FW_PlatformError* error)
- {
- FW_HString rep = ::StringRep_Clone(self, error);
- if (*error)
- return 0;
-
- ::FW_PrimitiveCopyMemory(rep->fParams.fTextStart+position+numberBytes,
- rep->fParams.fTextStart+position,
- rep->fParams.fTextByteLength-position-numberBytes);
-
- ::FW_TextParams_SetLength(&rep->fText,
- rep->fParams.fTextByteLength-numberBytes,
- &rep->fParams, error);
- if (*error)
- return 0;
-
- return rep;
- }
-
- //----------------------------------------------------------------------------------------
- // FW_PrivString_Truncate
- //----------------------------------------------------------------------------------------
-
- FW_HString FW_PrivString_Truncate(FW_HString self,
- FW_BytePosition position,
- FW_PlatformError* error)
- {
- FW_HString rep = ::StringRep_Clone(self, error);
- if (*error)
- return 0;
-
- FW_PRIV_ASSERT(position>=0);
- FW_PRIV_ASSERT(position<=rep->fParams.fTextByteLength);
-
- ::FW_TextParams_SetLength(&rep->fText, position, &rep->fParams, error);
- if (*error)
- return 0;
-
- return rep;
- }
-
- //----------------------------------------------------------------------------------------
- // FW_PrivString_InsertBytes
- //----------------------------------------------------------------------------------------
-
- FW_HString FW_PrivString_InsertBytes(FW_HString self,
- const char* bytes,
- FW_ByteCount numberBytes,
- FW_BytePosition position,
- FW_PlatformError* error)
- {
- FW_HString rep = ::StringRep_Clone(self, error);
- CHECK_ERROR;
-
- FW_PRIV_ASSERT(bytes);
- FW_PRIV_ASSERT(numberBytes>=0);
- FW_PRIV_ASSERT(position>=0);
- FW_PRIV_ASSERT(position<=rep->fParams.fTextByteLength);
-
- // ensure string will have sufficient capacity
- ::FW_TextParams_SetCapacity(&rep->fText,
- rep->fParams.fTextByteLength+numberBytes,
- !rep->fUsingStaticBuffer,
- &rep->fParams, error);
- CHECK_ERROR;
- FW_PRIV_ASSERT(rep->fParams.fTextByteLength+numberBytes
- <= rep->fParams.fTextByteCapacity);
-
- // move end of string to new position and make room for insertion
- ::FW_PrimitiveCopyMemory(rep->fParams.fTextStart+position,
- rep->fParams.fTextStart+position+numberBytes,
- rep->fParams.fTextByteLength-position);
-
- // insert bytes into position in rep
- ::FW_PrimitiveCopyMemory(bytes,
- rep->fParams.fTextStart+position,
- numberBytes);
-
- // update for the new length
- ::FW_TextParams_SetLength(&rep->fText,
- rep->fParams.fTextByteLength+numberBytes,
- &rep->fParams, error);
- CHECK_ERROR;
-
- return rep;
- }
-
- //----------------------------------------------------------------------------------------
- // FW_PrivString_InsertODIText
- //----------------------------------------------------------------------------------------
-
- FW_HString FW_PrivString_InsertODIText(FW_HString self,
- ODIText* text,
- FW_BytePosition position,
- FW_PlatformError* error)
- {
- // No need for FW_ERR_TRY block
- FW_ODITextParams params;
- ::FW_TextParams_GetParams(text, ¶ms, error);
- if (*error)
- return 0;
-
- FW_HString result = ::FW_PrivString_InsertBytes(self,
- params.fTextStart,
- params.fTextByteLength,
- position,
- error);
- if (*error)
- return 0;
- return result;
- }
-
- //----------------------------------------------------------------------------------------
- // FW_PrivString_InsertStringRep
- //----------------------------------------------------------------------------------------
-
- FW_HString FW_PrivString_InsertStringRep(FW_HString self,
- FW_HString other,
- FW_BytePosition position,
- FW_PlatformError* error)
- {
- // No need for FW_ERR_TRY block
- FW_CHECK(other);
- FW_HString result = ::FW_PrivString_InsertBytes(self,
- other->fParams.fTextStart,
- other->fParams.fTextByteLength,
- position,
- error);
- if (*error)
- return 0;
- return result;
- }
-
- //----------------------------------------------------------------------------------------
- // FW_PrivString_ReplaceAllBytes
- //----------------------------------------------------------------------------------------
-
- FW_HString FW_PrivString_ReplaceAllBytes(FW_HString self,
- const char* bytes,
- FW_ByteCount numberBytes,
- FW_PlatformError* error)
- {
- FW_HString rep = ::StringRep_Clone(self, error);
- if (*error)
- return 0;
-
- // ensure string will have sufficient capacity
- ::FW_TextParams_SetCapacity(&rep->fText,
- numberBytes,
- !rep->fUsingStaticBuffer,
- &rep->fParams,
- error);
- if (*error)
- return 0;
-
- // Copy bytes into rep
- ::FW_PrimitiveCopyMemory(bytes, rep->fParams.fTextStart, numberBytes);
-
- // update for the new length
- ::FW_TextParams_SetLength(&rep->fText, numberBytes, &rep->fParams, error);
- if (*error)
- return 0;
-
- return rep;
- }
-
- //----------------------------------------------------------------------------------------
- // FW_PrivString_ReplaceAllODIText
- //----------------------------------------------------------------------------------------
-
- FW_HString FW_PrivString_ReplaceAllODIText(FW_HString self,
- ODIText* text,
- FW_PlatformError* error)
- {
- // No need for FW_ERR_TRY block
- FW_HString result;
-
- FW_ODITextParams params;
- ::FW_TextParams_GetParams(text, ¶ms, error);
- if (*error)
- return 0;
-
- result = ::FW_PrivString_ReplaceAllBytes(self,
- params.fTextStart,
- params.fTextByteLength,
- error);
- if (*error)
- return 0;
-
- ::FW_TextParams_SetLocale(&result->fText, params.fTextLocale, &result->fParams, error);
-
- return result;
- }
-
- //----------------------------------------------------------------------------------------
- // FW_PrivString_ReplaceAllStringRep
- //----------------------------------------------------------------------------------------
-
- FW_HString FW_PrivString_ReplaceAllStringRep(FW_HString self,
- FW_HString other,
- FW_PlatformError* error)
- {
- // No need for FW_ERR_TRY block
- FW_HString result = self;
- if (self != other)
- {
- if (self->fLocked)
- {
- result = ::FW_PrivString_ReplaceAllBytes(self,
- other->fParams.fTextStart,
- other->fParams.fTextByteLength,
- error);
- if (*error)
- return 0;
-
- ::FW_TextParams_SetLocale(&result->fText, other->fParams.fTextLocale, &result->fParams, error);
- }
- else
- {
- ::FW_PrivString_Acquire(other);
- ::FW_PrivString_Release(self);
- result = other;
- }
- }
- return result;
- }
-
- //----------------------------------------------------------------------------------------
- // FW_PrivString_AppendBytes
- //----------------------------------------------------------------------------------------
-
- FW_HString FW_PrivString_AppendBytes(FW_HString self,
- const char* bytes,
- FW_ByteCount numberBytes,
- FW_PlatformError* error)
- {
- // No need for FW_ERR_TRY block
- FW_HString result = ::FW_PrivString_InsertBytes(self,
- bytes,
- numberBytes,
- self->fParams.fTextByteLength,
- error);
- CHECK_ERROR;
- return result;
- }
-
- //----------------------------------------------------------------------------------------
- // FW_PrivString_AppendODIText
- //----------------------------------------------------------------------------------------
-
- FW_HString FW_PrivString_AppendODIText(FW_HString self,
- ODIText* text,
- FW_PlatformError* error)
- {
- // No need for FW_ERR_TRY block
- FW_HString result = ::FW_PrivString_InsertODIText(self,
- text,
- self->fParams.fTextByteLength,
- error);
- CHECK_ERROR;
- return result;
- }
-
- //----------------------------------------------------------------------------------------
- // FW_PrivString_AppendStringRep
- //----------------------------------------------------------------------------------------
-
- FW_HString FW_PrivString_AppendStringRep(FW_HString self,
- FW_HString other,
- FW_PlatformError* error)
- {
- // No need for FW_ERR_TRY block
- FW_HString result = ::FW_PrivString_InsertStringRep(self,
- other,
- self->fParams.fTextByteLength,
- error);
- if (*error)
- return 0;
- return result;
- }
-
- //----------------------------------------------------------------------------------------
- // FW_PrivString_PrependBytes
- //----------------------------------------------------------------------------------------
-
- FW_HString FW_PrivString_PrependBytes(FW_HString self,
- const char* bytes,
- FW_ByteCount numberBytes,
- FW_PlatformError* error)
- {
- // No need for FW_ERR_TRY block
- FW_HString result = ::FW_PrivString_InsertBytes(self, bytes, numberBytes, 0, error);
- if (*error)
- return 0;
- return result;
- }
-
- //----------------------------------------------------------------------------------------
- // FW_PrivString_PrependODIText
- //----------------------------------------------------------------------------------------
-
- FW_HString FW_PrivString_PrependODIText(FW_HString self,
- ODIText* text,
- FW_PlatformError* error)
- {
- // No need for FW_ERR_TRY block
- FW_HString result = ::FW_PrivString_InsertODIText(self, text, 0, error);
- if (*error)
- return 0;
- return result;
- }
-
- //----------------------------------------------------------------------------------------
- // FW_PrivString_PrependStringRep
- //----------------------------------------------------------------------------------------
-
- FW_HString FW_PrivString_PrependStringRep(FW_HString self,
- FW_HString other,
- FW_PlatformError* error)
- {
- // No need for FW_ERR_TRY block
- FW_HString result = ::FW_PrivString_InsertStringRep(self, other, 0, error);
- if (*error)
- return 0;
- return result;
- }
-
- //----------------------------------------------------------------------------------------
- // FW_PrivString_ExportCString
- //----------------------------------------------------------------------------------------
-
- void FW_PrivString_ExportCString(FW_HString self, char* buffer)
- {
- const char nul = 0;
- ::FW_PrivString_Retrieve(self, buffer, self->fParams.fTextByteLength, 0);
- buffer[self->fParams.fTextByteLength] = nul;
- }
-
- //----------------------------------------------------------------------------------------
- // FW_PrivString_ExportPascalString
- //----------------------------------------------------------------------------------------
-
- void FW_PrivString_ExportPascalString(FW_HString self, FW_PascalChar* buffer)
- {
- ::FW_PrivString_Retrieve(self, (char*)buffer+1, self->fParams.fTextByteLength, 0);
- buffer[0] = FW_PascalChar(self->fParams.fTextByteLength);
- }
-
- //----------------------------------------------------------------------------------------
- // FW_PrivString_ToUpper
- //----------------------------------------------------------------------------------------
-
- FW_HString FW_PrivString_ToUpper(FW_HString self, FW_PlatformError* error)
- {
- FW_HString rep = ::StringRep_Clone(self, error);
- if (*error)
- return 0;
- #ifdef FW_BUILD_MAC
- ::UppercaseText(rep->fParams.fTextStart,
- rep->fParams.fTextByteLength,
- rep->fParams.fTextLocale.fScriptCode);
- *error = ::ResError();
- if (*error)
- return 0;
- #elif defined FW_BUILD_WIN
- //??? Need an international implementation for windows
- for (int i=0; i<rep->fParams.fTextByteLength; i++)
- rep->fParams.fTextStart[i] = toupper(rep->fParams.fTextStart[i]);
- #endif
- return rep;
- }
-
- //----------------------------------------------------------------------------------------
- // FW_PrivString_ToLower
- //----------------------------------------------------------------------------------------
-
- FW_HString FW_PrivString_ToLower(FW_HString self, FW_PlatformError* error)
- {
- FW_HString rep = ::StringRep_Clone(self, error);
- if (*error)
- return 0;
- #ifdef FW_BUILD_MAC
- ::LowercaseText(rep->fParams.fTextStart,
- rep->fParams.fTextByteLength,
- rep->fParams.fTextLocale.fScriptCode);
- *error = ::ResError();
- if (*error)
- return 0;
- #elif defined FW_BUILD_WIN
- //??? Need an international implementation for windows
- for (int i=0; i<rep->fParams.fTextByteLength; i++)
- rep->fParams.fTextStart[i] = tolower(rep->fParams.fTextStart[i]);
- #endif
- return rep;
- }
-
- //----------------------------------------------------------------------------------------
- // RabinKarpSearch
- //
- // Reference: Introduction to Algorithms, By Corment, Leiserson & Rivest, page 860
- // Note: The standard library function strstr is possibly as efficient as RabinKarpSearch,
- // but unfortunately it assumes that both strings are NUL terminated ISO strings. ODIText
- // data structures are not necessarily NUL terminated, so we can't use strstr. Note also
- // that there are algorithms more efficient than Rabin-Karp, but they're more complicated
- // to implement, and unlikely to be significantly better than Rabin-Karp for most common
- // string searching tasks. Developers creating text engines will probably keep their text
- // in some other kind of data structure and will have their own searching functions.
- //----------------------------------------------------------------------------------------
-
- static unsigned long RadixPower(unsigned long radix,
- unsigned long power,
- unsigned long modulus)
- {
- unsigned long result = 1;
- while (--power > 0)
- result = (result * radix) % modulus;
- return result;
- }
-
- class CPrivCharIterator
- {
- public:
- CPrivCharIterator() {}
- CPrivCharIterator(const unsigned char* pos, int delta) : fPos(pos), fDelta(delta) {}
- CPrivCharIterator(const CPrivCharIterator& other) : fPos(other.fPos), fDelta(other.fDelta) {}
- void operator++() { fPos += fDelta; }
- operator const unsigned char*() const { return fPos; }
- int operator==(const CPrivCharIterator& other) const { return fPos==other.fPos; }
- int operator!=(const CPrivCharIterator& other) const { return fPos!=other.fPos; }
- void operator=(const CPrivCharIterator& other) { fPos=other.fPos; fDelta=other.fDelta; }
- unsigned char operator*() const { return *fPos; }
-
- private:
- const unsigned char* fPos;
- int fDelta;
- };
-
- const unsigned long kRadix = 256;
- const unsigned long kModulus = (1L<<24) - 3; // this is the largest prime less than 2**24
-
- static unsigned long RunValue(CPrivCharIterator& begin, CPrivCharIterator& limit)
- {
- unsigned long value = 0;
- CPrivCharIterator i;
- for (i=begin; i!=limit; ++i)
- {
- unsigned char c = *i;
- value = (kRadix*value + c) % kModulus;
- }
- return value;
- }
-
- static int PrivCompare(const CPrivCharIterator& pat,
- const CPrivCharIterator& window,
- const CPrivCharIterator& patLimit)
- {
- CPrivCharIterator i=pat;
- CPrivCharIterator w=window;
-
- while (i!=patLimit)
- {
- if (*i != *w)
- return 0;
- ++i;
- ++w;
- }
- return 1;
- }
-
- static long RabinKarpSearch(const unsigned char* text,
- const unsigned char* pat,
- long n, // length of text
- long m, // length of pat
- int searchForwards = 1)
- {
- long shift = -1;
- if (m <= 0)
- return shift;
- if (m > n)
- return shift;
-
- const unsigned long h = RadixPower(kRadix, m, kModulus);
-
- CPrivCharIterator wbegin;
- CPrivCharIterator wend;
- CPrivCharIterator wlimit;
- CPrivCharIterator pbegin;
- CPrivCharIterator pend;
-
- if (searchForwards)
- {
- wbegin = CPrivCharIterator(text, 1);
- wend = CPrivCharIterator(text+m, 1);
- wlimit = CPrivCharIterator(text+n-m+1, 0);
- pbegin = CPrivCharIterator(pat, 1);
- pend = CPrivCharIterator(pat+m, 1);
- }
- else
- {
- wbegin = CPrivCharIterator(text+n-1, -1);
- wend = CPrivCharIterator(text+n-m-1, -1);
- wlimit = CPrivCharIterator(text-1, 0);
- pbegin = CPrivCharIterator(pat+m-1, -1);
- pend = CPrivCharIterator(pat-1, -1);
- }
-
- unsigned long p = RunValue(pbegin, pend);
- unsigned long t = RunValue(wbegin, wend);
-
- while (wbegin != wlimit)
- {
- if ((p == t) && PrivCompare(pbegin, wbegin, pend))
- {
- shift = (const unsigned char*)wbegin - text;
- break;
- }
-
- unsigned char c = *wbegin;
- ++wbegin;
-
- if (wbegin != wlimit)
- {
- // subtract out the oldest character
- unsigned long z = (c*h) % kModulus;
- if (t >= z)
- t -= z;
- else
- t = t + kModulus - z;
-
- // add in the new character
- c = *wend;
- t = (kRadix*t + c) % kModulus;
-
- ++wend;
- FW_ASSERT(t == RunValue(wbegin, wend));
- }
-
- }
-
- if (shift != -1 && !searchForwards)
- {
- shift -= (m-1);
- }
-
- return shift;
- }
-
- //----------------------------------------------------------------------------------------
- // FW_PrivString_FindCharacter
- //----------------------------------------------------------------------------------------
-
- static Boolean PrivIsCharacterMatch(FW_HString self,
- FW_ByteCount i,
- FW_ByteCount *foundPosition)
- {
- Boolean result = false;
- FW_PlatformError error = 0;
- if (::FW_LocaleIsSingleByte(self->fParams.fTextLocale)
- || FW_TextParams_IsCharacterStart(&self->fText, 0, i, &error))
- {
- FW_ASSERT(error == 0);
- result = true;
- *foundPosition = i;
- }
- return result;
- }
-
- const FW_LChar kLowByteMask = 0x0FF;
- const FW_LChar kLowByteAntiMask = ~kLowByteMask;
-
- static void LCharToBytes(FW_LChar c, char* bytes)
- {
- *bytes++ = c >> 8;
- *bytes = c & kLowByteMask;
- }
-
- FW_Boolean FW_PrivString_FindCharacter(FW_HString self,
- FW_LChar character,
- FW_ByteCount *foundPosition,
- FW_ByteCount startPosition,
- FW_FindDirection direction)
- {
- FW_Boolean result = false; // assume failure to find character
-
- if ((character & kLowByteAntiMask) == 0)
- {
- // If the character we're searching for is a single byte character
- // then we can use a simple char-by-char scan
- register char c = (char) (character & kLowByteMask);
- register const char* text = self->fParams.fTextStart;
- if (direction == FW_kForwards)
- {
- for (FW_ByteCount i=startPosition; i<self->fParams.fTextByteLength; i++)
- {
- if ((text[i] == c) && (result=PrivIsCharacterMatch(self, i, foundPosition)) == true)
- break;
- }
- }
- else
- {
- for (FW_ByteCount i=startPosition; i>=0; i--)
- {
- if ((text[i] == c) && (result=PrivIsCharacterMatch(self, i, foundPosition)) == true)
- break;
- }
- }
- }
- else if (!::FW_LocaleIsSingleByte(self->fParams.fTextLocale))
- {
- // If we've gotten to here, we have a locale that is not single-byte,
- // and we're searching for a character that is not a single-byte character.
- // We handle that case by making a single-character string and then doing
- // a substring search.
- char bytes[2];
- LCharToBytes(character, bytes);
- *foundPosition = RabinKarpSearch((const unsigned char*)self->fParams.fTextStart+startPosition,
- (const unsigned char*)bytes,
- self->fParams.fTextByteLength-startPosition,
- 2, // length of pat
- direction == FW_kForwards);
- result = *foundPosition != -1;
- if (result)
- {
- *foundPosition += startPosition;
- }
- }
- else
- {
- // If we've gotten to here, we're trying to find a multi-byte character
- // in a single-byte locale string. By definition, that must fail, right?
- // Since result is already set to false, we don't have to do anything.
- }
- return result;
- }
-
- //----------------------------------------------------------------------------------------
- // FW_PrivString_FindSubString
- //----------------------------------------------------------------------------------------
-
- FW_Boolean FW_PrivString_FindSubString(FW_HString self,
- FW_HString subString,
- FW_ByteCount *foundPosition,
- FW_ByteCount startPosition)
- {
- *foundPosition = RabinKarpSearch((const unsigned char*) self->fParams.fTextStart+startPosition,
- (const unsigned char*) subString->fParams.fTextStart,
- self->fParams.fTextByteLength-startPosition,
- subString->fParams.fTextByteLength);
- if (*foundPosition != -1)
- *foundPosition += startPosition;
- return (*foundPosition != -1);
- }
-
- //----------------------------------------------------------------------------------------
- // FW_PrivString_Substitute
- //----------------------------------------------------------------------------------------
-
- FW_HString FW_PrivString_Substitute(FW_HString self,
- FW_HString searchString,
- FW_HString substitutionString,
- FW_Boolean* wasReplaced,
- FW_PlatformError* error)
- {
- FW_HString rep = self;
- FW_ByteCount foundPosition;
- *wasReplaced = false;
- if (::FW_PrivString_FindSubString(self, searchString, &foundPosition, 0))
- {
- FW_HString temp;
- rep = ::StringRep_Clone(self, error);
- temp = ::FW_PrivString_Delete(rep,
- FW_PrivString_GetByteLength(searchString),
- foundPosition,
- error);
- if (*error)
- return 0;
- FW_PRIV_ASSERT(temp == rep);
- temp = ::FW_PrivString_InsertStringRep(rep,
- substitutionString,
- foundPosition,
- error);
- if (*error)
- return 0;
- FW_PRIV_ASSERT(temp == rep);
- *wasReplaced = true;
- }
- return rep;
- }
-
- //----------------------------------------------------------------------------------------
- // FW_WinHackTextCompare
- //----------------------------------------------------------------------------------------
-
- #ifdef FW_BUILD_WIN
- // This is a hack. It does a byte level compare.
- // We need the proper text compare routine for windows
- static FW_StringCompareResult FW_WinHackTextCompare(
- const char *p1,
- const char *p2,
- FW_ByteCount len1,
- FW_ByteCount len2)
- {
- FW_ByteCount len = len1;
- int result = 0; // assume strings are equal
- if (len1 < len2)
- result = -1; // now assume string 1 is less because it's shorter
- else if (len1 > len2)
- {
- result = 1; // now assume string 1 is greater because it's longer
- len = len2;
- }
-
- const unsigned char* s1 = (const unsigned char*) p1;
- const unsigned char* s2 = (const unsigned char*) p2;
-
- unsigned char c1 = *s1;
- unsigned char c2 = *s2;
-
- while (len-- > 0)
- {
- c1 = *s1++;
- c2 = *s2++;
- if (!c1)
- break;
- if (c1 != c2)
- break;
- }
-
- if (len > 0)
- {
- if (c1 < c2)
- result = -1;
- if (c1 > c2)
- result = 1;
- }
-
- return result;
- }
- #endif
-
- //----------------------------------------------------------------------------------------
- // FW_PrivString_Compare
- //----------------------------------------------------------------------------------------
-
- FW_StringCompareResult FW_PrivString_Compare(FW_HString string1, FW_HString string2)
- {
- #ifdef FW_BUILD_MAC
- return ::TextOrder(string1->fParams.fTextStart,
- string2->fParams.fTextStart,
- string1->fParams.fTextByteLength,
- string2->fParams.fTextByteLength,
- string1->fParams.fTextLocale.fScriptCode,
- string2->fParams.fTextLocale.fScriptCode,
- string1->fParams.fTextLocale.fLangCode,
- string2->fParams.fTextLocale.fLangCode);
- #else
- return ::FW_WinHackTextCompare(string1->fParams.fTextStart,
- string2->fParams.fTextStart,
- string1->fParams.fTextByteLength,
- string2->fParams.fTextByteLength);
- #endif
- }
-
- //----------------------------------------------------------------------------------------
- // Number Conversion Functions
- //----------------------------------------------------------------------------------------
-
- //----------------------------------------------------------------------------------------
- // FW_PrivString_SignedIntegerToDecimalString
- //----------------------------------------------------------------------------------------
-
- FW_HString FW_PrivString_SignedIntegerToDecimalString(FW_HString self,
- long integer,
- FW_PlatformError* error)
- {
- // we allow for the possiblity that longs are more than 32 bits
- const int kMaxDigits = 32;
- char string[kMaxDigits];
- FW_Boolean negative = integer < 0;
- if (negative)
- integer = -integer;
- const char kZero = '0';
- const long int kBase = 10;
-
- int i = kMaxDigits;
-
- if (integer == 0)
- {
- --i;
- string[i] = kZero;
- }
- else
- {
- while (integer > 0)
- {
- --i;
- string[i] = (char) (integer % kBase) + kZero;
- integer = integer / kBase;
- }
- }
-
- if (negative)
- {
- --i;
- string[i] = '-';
- }
-
- return ::FW_PrivString_ReplaceAllBytes(self, string+i, kMaxDigits-i, error);
- }
-
- //----------------------------------------------------------------------------------------
- // FW_PrivString_UnsignedIntegerToDecimalString
- //----------------------------------------------------------------------------------------
-
- FW_HString FW_PrivString_UnsignedIntegerToDecimalString(FW_HString self,
- unsigned long integer,
- FW_PlatformError* error)
- {
- // we allow for the possiblity that longs are more than 32 bits
- const int kMaxDigits = 32;
- char string[kMaxDigits];
- const char kZero = '0';
- const long int kBase = 10;
-
- int i = kMaxDigits;
-
- if (integer == 0)
- {
- --i;
- string[i] = kZero;
- }
- else
- {
- while (integer > 0)
- {
- --i;
- string[i] = (char) (integer % kBase) + kZero;
- integer = integer / kBase;
- }
- }
-
- return ::FW_PrivString_ReplaceAllBytes(self, string+i, kMaxDigits-i, error);
- }
-
- //----------------------------------------------------------------------------------------
- // FW_PrivString_UnsignedIntegerToHexadecimalString
- //----------------------------------------------------------------------------------------
-
- FW_HString FW_PrivString_UnsignedIntegerToHexadecimalString(FW_HString self,
- unsigned long integer,
- FW_PlatformError* error)
- {
- // we allow for the possiblity that longs are more than 32 bits
- const int kMaxDigits = 32;
- char string[kMaxDigits];
- const char kZero = '0';
- const char kA = 'A';
- const long int kBase = 16;
-
- int i = kMaxDigits;
-
- if (integer == 0)
- {
- --i;
- string[i] = kZero;
- }
- else
- {
- while (integer > 0)
- {
- --i;
- unsigned long digit = (integer % kBase);
- if (digit < 10)
- string[i] = (char) digit + kZero;
- else
- string[i] = (char) digit + kA;
- integer = integer / kBase;
- }
- }
-
- return ::FW_PrivString_ReplaceAllBytes(self, string+i, kMaxDigits-i, error);
- }
-
- //----------------------------------------------------------------------------------------
- // FW_PrivString_DecimalStringToSignedInteger
- //----------------------------------------------------------------------------------------
-
- long FW_PrivString_DecimalStringToSignedInteger(FW_HString self)
- {
- const char* string = self->fParams.fTextStart;
- const char* last = string + self->fParams.fTextByteLength;
- FW_Boolean negative = (*string == '-');
- const char kZero = '0';
- const char kNine = '9';
- const long int kBase = 10;
- long result = 0;
-
- if (negative)
- ++string;
-
- while ((string<last) && (*string >= kZero) && (*string <= kNine))
- {
- long digit = *string - kZero;
- result = result*kBase + digit;
- ++string;
- }
-
- if (negative)
- result = -result;
-
- return result;
- }
-
- //----------------------------------------------------------------------------------------
- // FW_PrivString_DecimalStringToUnsignedInteger
- //----------------------------------------------------------------------------------------
-
- unsigned long FW_PrivString_DecimalStringToUnsignedInteger(FW_HString self)
- {
- const char* string = self->fParams.fTextStart;
- const char* last = string + self->fParams.fTextByteLength;
- const char kZero = '0';
- const char kNine = '9';
- const unsigned long int kBase = 10;
- unsigned long result = 0;
-
- while ((string<last) && (*string >= kZero) && (*string <= kNine))
- {
- unsigned long digit = *string - kZero;
- result = result*kBase + digit;
- ++string;
- }
-
- return result;
- }
-
- //----------------------------------------------------------------------------------------
- // IsHex
- //----------------------------------------------------------------------------------------
-
- static FW_Boolean IsHex(const char hexChar, unsigned long& digit)
- {
- FW_Boolean result = false;
- if (hexChar>='0' && hexChar<='9')
- {
- result = true;
- digit = hexChar - '0';
- }
- else if (hexChar>='a' && hexChar<='f')
- {
- result = true;
- digit = hexChar - 'a';
- }
- else if (hexChar>='A' && hexChar<='F')
- {
- result = true;
- digit = hexChar - 'A';
- }
- return result;
- }
-
- //----------------------------------------------------------------------------------------
- // FW_PrivString_HexadecimalStringToUnsignedInteger
- //----------------------------------------------------------------------------------------
-
- unsigned long FW_PrivString_HexadecimalStringToUnsignedInteger(FW_HString self)
- {
- const char* string = self->fParams.fTextStart;
- const char* last = string + self->fParams.fTextByteLength;
- const unsigned long int kBase = 16;
- unsigned long result = 0;
- unsigned long digit;
-
- while ((string<last) && IsHex(*string, digit))
- {
- result = result*kBase + digit;
- ++string;
- }
-
- return result;
- }
-
- //----------------------------------------------------------------------------------------
- // FW_PrivString_DoubleToString
- //----------------------------------------------------------------------------------------
-
- FW_HString FW_PrivString_DoubleToString(FW_HString self,
- double value,
- short fractionalDigits,
- FW_PlatformError* error)
- {
- #ifdef FW_BUILD_MAC
- decform form = {FIXEDDECIMAL, 0, fractionalDigits};
- decimal intermediate;
- char buffer[DECSTROUTLEN];
-
- num2dec(&form, value, &intermediate);
- dec2str(&form, &intermediate, buffer);
-
- return ::FW_PrivString_ReplaceAllBytes(self,
- buffer,
- FW_PrimitiveStringLength(buffer),
- error);
- #elif defined FW_BUILD_WIN
- FW_PRIV_ASSERT(Not_Yet_Implemented);
- return (FW_HString) NULL;
- #endif
- }
-
- //----------------------------------------------------------------------------------------
- // FW_PrivString_StringToDouble
- //----------------------------------------------------------------------------------------
-
- double FW_PrivString_StringToDouble(FW_HString self)
- {
- double result = 0;
-
- #if defined FW_BUILD_MAC
- // See IM-PPC Numerics for documentation on str2dec (chap 9, "Conversion Functions")
- decimal intermediate; // On return, a pointer to the decimal structure containing the decimal number.
- short ix = 0; // On entry, the starting position in the string.
- // On return, one greater than the position of the last character in the
- // string that was parsed if the entire string was not converted successfully.
- short vp; // On return, a Boolean argument indicating the success of the function.
- // If the entire string was parsed, vp is true. If part of the string was parsed,
- // vp is false and ix indicates where the function stopped parsing.
- char buffer[256];
- FW_PrivString_ExportCString(self, buffer);
- str2dec(buffer, &ix, &intermediate, &vp);
- result = dec2num(&intermediate);
- #elif defined FW_BUILD_WIN
- FW_PRIV_ASSERT(Not_Yet_Implemented);
- #endif
-
- return result;
- }
-
-